<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>初级</title>
</head>

<body>

    <script>
        /* 解构赋值 */

        /* 数组 */
        // 按照ES6规范 允许在对象和数组中提取值 对变量赋值 即为解构(Destructuring)
        /**
         *  注意点：
         *      1.模式匹配 等号两边结构一致
         *      2.如果不匹配，则直接忽略缺失部分 两边都忽略 即不完全结构
         *      3.如果左边最后一个变量出现三个小点，则右边对应位置之后的所有数据都会组成一个数组
         *      4.结构不成功，对应变量的值就等于undefined
         *      5.可初始化赋值 并且是惰性求值
         * */

        // 完全解构
        let [a, b, c] = [1, 2, 3]
        console.log(a, b, c) // 1 2 3

        let [foo, [
            [bar], baz
        ]] = [1, [
            [2], 3
        ]]
        console.log(foo, bar, baz) // 1 2 3

        // 不完全解构
        let [, , third] = ['ni', 'wo', 'ta']
        console.log(third) // ta

        let [x, , y] = [1, 2, 3]
        console.log(x, y) // 1 3

        // 解构数组
        let [head, ...tail] = [1, 2, 3, 4, 5]
        console.log(head, tail) // 1 [2,3,4,5]

        let [m, n, ...q] = [6]
        console.log(m, n, q) // 6 undefined []

        // let [e,...v,g] = [1,2,3,4,5]
        // 报错 Rest element must be last element 其余元素必须是最后一个元素

        function f() {
            console.log('aaa')
        }

        let [j = f()] = [1]
        console.log(j) // 1 不会执行函数

        /* 对象 */
        // 唯一与数组不同的是 变量名必须和对象属性名一致 否则无法取值 即为undefined

        let {
            ca,
            ba,
            za
        } = {
            ca: 'ni',
            ba: 'wo',
            va: 'ta'
        }
        console.log(ca, ba, za) // ni wo undefined

        // 好处 可以将对象的某些方法提取出来 简化引用
        let {
            sin,
            cos
        } = Math // 将数学对象的sin cos方法通过变量sin cos来引用

        const {
            log
        } = console
        log('我简化名称了！')

        // 如果变量名与属性名不一致
        let obj = {
            first: 1,
            last: 2
        }
        let {
            first: one,
            last: two
        } = obj
        log(one, two) // 1 2

        // 嵌套解构
        let pbj = {
            p: ['hello', {
                la: 'la'
            }]
        }
        let {
            p,
            p: [xl, {
                la
            }]
        } = pbj
        log(p, xl, la) // 等同结构

        let lsp = {
            lao: {
                se: {
                    pi: 'pi',
                    ha: '哈'
                }
            }
        }

        let {
            lao,
            lao: {
                se,
                se: {
                    pi,
                    ha
                }
            }
        } = lsp
        log(lao, se, pi, ha)

        // 已声明的变量要用作结构的正确语法 用小括号防止JavaScript解析器将{bala}解析为代码块
        let bala;
        ({
            bala
        } = {
            bala: 1
        })

        /* 字符串 */
        // 字符串会被转化为一个类数组对象 具有length属性

        const [aa, bb, cc, dd, ee] = 'hello'
        const {
            length: len
        } = 'hello'
        log(aa, bb, cc, dd, ee, len) // h e l l o 5

        /* 数值和布尔值 */
        // 只要右边非对象或数组 就先转化为临时对象
        // null 和 undefined 无法转换对象 所以无法结构赋值

        let {
            toString: str
        } = 123
        let {
            toString: st
        } = true
        log(str, st)

        /* 函数参数 */
        // 结构参数 并非数组
        function add([x, y]) {
            return x + y
        }

        log(add([1, 2]))

        //  对参数解构 能解构就用解构值 不能就初始化为0
        function move({
            x = 0,
            y = 0
        } = {}) {
            log([x, y])
        }

        move({
            x: 1,
            y: 2
        }) // [1,2]
        move({
            x: 1
        }) // [1,0]
        move({}) // [0,0]
        move() // [0,0]

        // 为函数指定默认参数值 而不是为x,y变量指定默认值
        function moves({
            x,
            y
        } = {
            x: 0,
            y: 0
        }) {
            log([x, y])
        }

        moves({
            x: 1,
            y: 2
        }) // [1,2]
        moves({
            x: 1
        }) // [1,undefined]
        moves({}) // [undefined,undefined]
        moves() // [0,0]

        /* 解构赋值注意点 */
        // 圆括号问题 圆括号有运算优先级 声明语句最好不要在模式中出现()
        // 赋值语句可以出现()
        // [(b)] = [3]  ({p:(d)}={})

        /* 常见应用场景 */
        // 交换变量值
        let l = 1
        let k = 2;
        [l, k] = [k, l]
        log(l, k) // 2 1

        // 从函数返回对象中提取多个值
        // 返回数组
        function ex() {
            return [1, 2, 3]
        }
        let [we, qe, re] = ex()
        console.log([we, qe, re]) // [1,2,3]

        // 返回对象
        function objx() {
            return {
                aaa: '嗷嗷嗷',
                bbb: '不不不'
            }
        }
        let {
            aaa,
            bbb
        } = objx()
        console.log(aaa, bbb) // 嗷嗷嗷 不不不

        // 提取JSON数据
        let jsonDate = {
            "id": 45,
            "status": "OK",
            "data": [545, 656]
        }
        let {
            id,
            status,
            data: number
        } = jsonDate
        log(id, status, number) // 45 OK [545,656]

        // 函数参数的定义
        function f1([x, y, z]) {}
        f1([1, 2, 3]) // 有序参数

        function f2({
            x,
            y,
            z
        }) {}
        f2({
            z: 3,
            y: 2,
            x: 1
        }) // 无序参数
    </script>
</body>

</html>